#!/usr/bin/env python
# vi: et sw=2
#
# Gnome PulseAudio applet
# Copyright (C) 2009  Harry Karvonen
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
#
#
# Author: Harry Karvonen <harry.karvonen@gmail.com>
#         Aki Rautio <aki.rautio@gmail.com>
#

import pygtk
pygtk.require('2.0')

import gtk , gobject
import gnomeapplet
import gnome.ui
import os.path
import sys
import gconf

import locale
import gettext

## Global values
APPLET_VERSION = '0.3'
APPLET_DIR = '/usr/share/gnome-pulse-applet'
LOCALE_DIR = '/usr/share/locale'
ICON_DIR = '/usr/share/icons/hicolor/16x16/apps'
CONF_DIR = '/apps/gnome-pulse-applet'
MUTED_ICON = '/usr/share/icons/gnome/scalable/actions/stop.svg'
ICON_THEME = gtk.icon_theme_get_default()

## Localization
locale.setlocale(locale.LC_ALL, '')

gettext.bindtextdomain('gnome-pulse-applet',LOCALE_DIR)
gettext.textdomain('gnome-pulse-applet')

_ = gettext.gettext

locale.bindtextdomain('gnome-pulse-applet',LOCALE_DIR)
locale.textdomain('gnome-pulse-applet')

sys.path.append(APPLET_DIR + '/py/')
from GnomePulseAppletInterface import PulseObj, \
                                      PulseSink, \
                                      PulseSinkInfo, \
                                      PulseSinkInputInfo, \
                                      PulseVolume
from preferences import Preferences
from xml_icons import StreamIcons
from conf import Config


################################################################################
#
# PulseApplet
class PulseApplet:

  ##############################################################################
  #
  # Init
  def __init__(self, applet, iid):

    # Variables
    self.about         = None
    self.applet        = applet
    self.dock          = None
    self.icon          = gtk.Image()
    self.preferences   = None
    self.pulse         = None
    self.pop           = False
    self.selected_sink = None
    self.tooltips      = gtk.Tooltips()
    self.config        = Config()
    self.panel_size    = 24
    self.min_size      = 23
    self.allow_quit    = False
    self.menu          = None
    
    # init
    gnome.init("Pulse Applet", self.applet.version)

    ## Pulse connect
    self.pulse = PulseObj("PulseApplet", None, True)
                         # Connects pulseaudio (None == localhost)
    #self.pulse = PulseObj("noHostFound", True) # Connects pulseaudio

    # Check if the sink exists
    if int(self.config.get("sink")) >= len(self.pulse.pulse_sink_list()):
      self.config.save("sink", 0)

    ## Check which sink will be showed in Dock ( Stream list )
    self.updateSelectedSink()

    ## Create about window
    self.create_about()
    
    ## Applet icon    
    self.updateIcon()
    self.applet.add(self.icon)

    ## If applet is used in Applet mode (not window or tray)
    if isinstance(self.applet, gnomeapplet.Applet):
      self.applet.connect("change-background",
                        self.change_background)
      self.applet.connect("size-allocate",
                        self.size_allocate_event)
    
    ## Make trayicon connections
    self.applet.connect("button-press-event",
                        self.applet_button_press_event)
    self.applet.connect("key-press-event",
                        self.applet_key_press_event)
    self.applet.connect("scroll-event",
                        self.applet_scroll_event)
    self.applet.connect("destroy",
                        self.cleanup, self.applet)
    self.applet.show_all()

    ## Create dock (the stream list)
    self.dock = PulseAppletDock(self,self.config)
    self.dock.window.connect("button-press-event",
                             self.applet_button_press_event)
    self.dock.window.connect("key-press-event",
                             self.applet_key_press_event)

    return
  
  ###
  #  
  # Applet and dock functions

  ## Allocate new size (used when applets size changes)

  def size_allocate_event(self, widget, allocation):
    orient = self.applet.get_orient()

    #print self.panel_size
    #print "a", allocation.height

    #if isinstance(widget, gnomeapplet.Applet):
    #  print "b", widget.get_parent().get_allocation().height
      
    if orient in (gnomeapplet.ORIENT_UP, gnomeapplet.ORIENT_DOWN):
      if self.panel_size == allocation.height:
        return
      self.panel_size = allocation.height
    else:
      if self.panel_size == allocation.width:
        return
      self.panel_size = allocation.width

    ## FIXME: Following panel size change will cause problem when window mode
    ## is used. 

    # setting minimum size of applet to 23
    ##if self.panel_size < self.min_size:
    ##  self.panel_size = self.min_size

    #print "size_alloc:", self.panel_size;

    self.updateIcon()

    return
  
  ###

  ## Make Applets icons background match with panels background
  
  def change_background(self,panelapplet,backgroundtype,color,pixmap):
    self.applet.modify_bg(gtk.STATE_NORMAL, color)
    self.icon.modify_bg(gtk.STATE_NORMAL, color)
    
    if pixmap is not None:
      s1 = self.applet.get_style()
      s2 = self.applet.get_style()
      
      s1.bg_pixmap[gtk.STATE_NORMAL] = pixmap
      s2.bg_pixmap[gtk.STATE_NORMAL] = pixmap
    
  ###

  ## Pop up/down Dock ( Stream list )

  def pop_dock(self):
    #print "pop_dock: Called"

    if self.pop:
      self.pop_dock_down()
    else:
      self.pop_dock_up()

    return

  ###

  ## Popup instance

  def pop_dock_up(self):
    #print "pop_dock_up: Called"

    if self.pop:
      return

    if not self.pulse.connected:
      self.pulse.reconnect()

    self.dock.update()
    self.applet.set_state(gtk.STATE_SELECTED)

    self.dock.window.show_all()

    gtk.gdk.keyboard_grab(self.dock.window.window, True)
    gtk.gdk.pointer_grab(self.dock.window.window, True,
        gtk.gdk.BUTTON_PRESS_MASK |
        gtk.gdk.BUTTON_RELEASE_MASK |
        gtk.gdk.POINTER_MOTION_MASK
    )

    self.pop = True

    #print "last", self.dock.last_stream.get_size()
    #print "last", self.dock.last_stream.window.get_size()
    #print "wind", self.dock.window.get_size()
    return

  ###

  ## Pop down instance

  def pop_dock_down(self):
    #print "pop_dock_down: Called"

    if not self.pop:
      return

    self.applet.grab_remove()
    self.dock.window.hide_all()

    self.applet.set_state(gtk.STATE_NORMAL)

    gtk.gdk.keyboard_ungrab()
    gtk.gdk.pointer_ungrab()

    self.pop = False

    return

  ###

  ## Cleanup applet

  def cleanup(self, event, widget):
    #self.pulse.pulse_disconnect()
    del self.applet
    return

  ###

  ## Check what sinks streams dock will show

  def updateSelectedSink(self):
    if self.pulse.connected:
      self.selected_sink = self.pulse.pulse_sink_list()[int(
                                                      self.config.get("sink"))]
    else:
      self.selected_sink = None

  ###

  ## Updates applet icon ( Icon has tree stated depending on wheater PA
  ## is connected and is master volume mute or o not.)

  def updateIcon(self):
    
    self.updateSelectedSink()

    #print "updateIcon"
    if self.pulse.connected:
      if self.selected_sink.mute == 0:
        self.icon.set_from_pixbuf(self.load_icon(False, self.panel_size-4)) 
        self.tooltips.set_tip(self.icon,_("Master volume") + ": " + 
                str(self.selected_sink.volume.getVolume()) + "%")
      else:
        self.icon.set_from_pixbuf(self.load_icon(True, self.panel_size-4)) 
        self.tooltips.set_tip(self.icon,_("Master volume") + ": " + _("Muted"))
    else:
      # FIXME it would be nice have own icon for not connected state
      self.icon.set_from_pixbuf(self.load_icon(False, self.panel_size-4))
      self.tooltips.set_tip(self.icon,_("No connection")) 

    self.icon.show_all()

    return

  ###

  ## Loads default UnMute/Mute icon for applet
  
  def load_icon(self,isMuted,size):
    if isMuted:
      pixbuf = ICON_THEME.load_icon('audio-volume-muted',
                                    size,
                                    gtk.ICON_LOOKUP_USE_BUILTIN)
    else:
      pixbuf = ICON_THEME.load_icon('audio-volume-medium',
                                    size,
                                    gtk.ICON_LOOKUP_USE_BUILTIN)
    return pixbuf


  ###
  ## 
  ## Events

  ## Scroll event ( When scrollwheel is used over applet)
  
  def applet_scroll_event(self, widget, event):
    #print event.type, event.direction, event.state
    if not self.pulse.connected:
      return False

    self.updateSelectedSink()

    volume = self.selected_sink.volume

    if event.direction == gtk.gdk.SCROLL_UP or \
       event.direction == gtk.gdk.SCROLL_RIGHT:
      volume.incVolume(10)
    elif event.direction == gtk.gdk.SCROLL_DOWN or \
         event.direction == gtk.gdk.SCROLL_LEFT:
      volume.incVolume(-10)

    self.pulse.pulse_set_sink_volume(self.selected_sink.index, volume)

    self.tooltips.set_tip(self.icon,_("Master volume") + ": " + 
             str(self.selected_sink.volume.getVolume()) + "%")

    return True

  ###

  ## Mouse button actions ( When Applet has been clicked)

  # First button will open Dock, second open default menu
  # and third Unmutes/mutes selected sinks master volume

  def applet_button_press_event(self, widget, event):

    self.updateIcon()

    #print "applet_button_press_event: Called"
    # If we want exit by clicking somewhere
    # FIXME This may be unused
    if event.window != widget.window and \
       event.type == gtk.gdk.BUTTON_PRESS:
      self.pop_dock_down()
      return True

    elif event.window == widget.window:
      if event.button == 3:
        if self.pop:
          self.pop_dock_down()
          return True
        else:
          self.create_menu()
      elif event.button == 2:
        
        if self.selected_sink.mute == 0:
          self.pulse.pulse_mute_sink(self.selected_sink.index)
        else:
          self.pulse.pulse_unmute_sink(self.selected_sink.index)

        self.updateIcon()

      elif event.button == 1:
        self.pop_dock()

        return True

    return False

  ###

  ## Key button actions
 
  # If Space, Enter or Esc is pressed when dock is active, dock will
  # be hidden.

  def applet_key_press_event(self, widget, event):
    #print "applet_key_press_event: Called"

    #print widget, event.type
    #print event.keyval
    if not event.type == gtk.gdk.KEY_PRESS:
      return False

    if event.keyval == 32: # Space
      self.pop_dock()
    elif event.keyval == 65293: # Enter
      self.pop_dock()
    elif event.keyval == 65307: # ESC
      self.pop_dock_down()

    return True

  ###
  ## 
  ## Preferences and About dialog functions

  ## Create about Dialog
  def create_about(self, data = None):
    self.about = gtk.AboutDialog()
    self.about.set_name(_('Gnome PulseAudio applet'))
    self.about.set_version(self.applet.version)
    self.about.set_comments(_('Control each PulseAudio audio streams'))
    self.about.set_authors([
        'Harry Karvonen',
        'Aki Rautio'
    ])
    self.about.set_artists(['Petri Partio'])
    self.about.set_website("http://code.google.com/p/gnome-pulse-applet/")
    self.about.set_translator_credits(_("translator-credits"))
    self.about.connect('response', self.about_response)
    self.about.connect('close', self.about_close)
    return

  ###
  
  ## Show about dialog

  def show_about(self,event,data = None):
    self.about.show()
    return
  
  ###
  
  ## Hide about dialog when close button is pressed (in upper right corner)

  def about_response(self, dialog, response):
    if response < 0:
      dialog.hide()
      dialog.emit_stop_by_name('response')
    return

  ###

  ## Hide about dialog when Close button is pressed 

  def about_close(self, widget):
    self.about.hide()
    return True

  ###

  ## Show Preferences. If it not exist, create it

  def show_preferences(self, event, data = None):
    if self.preferences == None:    
      self.preferences   = Preferences(self.pulse)
    else:
      self.preferences.open_window()    
    return

  ###

  ## This is for tray icon

  def __quit(self, widget, data = None):
    gtk.main_quit()

  ###

  ## Create menu
  def create_menu(self):
    self.verbs = [("about", self.show_about),
                  ("preferences", self.show_preferences)]
    labels = ["_About",
              "_Preferences"]

    # Create menu if menu isn't already created
    if self.menu == None:
      self.menu = """
<popup name="button3">
  <menuitem name="about" verb="about" _label="%s" pixtype="stock" 
             pixname="gtk-about"/>
  <menuitem name="preferences" verb="preferences" _label="%s" pixtype="stock" 
            pixname="gtk-preferences"/>
      """
      # FIXME Add gnome-volume-configuration link to here      
      
      # FIXME Add "Layout position" option here
      
      # FIXME Add "Sink selection" option here

      # FIXME After previous fixes has been made reorganize Preferences window       

      # Add quit if allowed
      if self.allow_quit:
        self.verbs.append(("quit", self.__quit))
        labels.append("_Quit")

        self.menu += """
  <menuitem name="quit" verb="quit" _label="%s"
            pixtype="stock" pixname="gtk-quit"/>
        """

      # End menu xml
      self.menu += """
</popup>
      """

      # Place names
      self.menu = self.menu % tuple(labels)

      # Create the menu
      self.applet.setup_menu(self.menu, self.verbs, None)

    return

################################################################################
#
# PulseAppletDock

class PulseAppletDock:
  def __init__(self, pulseApplet,config):

    # Variables
    self.config            = config
    self.pulseApplet       = pulseApplet
    self.pulseAppletStream = None
    self.streams           = None
    self.window            = gtk.Window(gtk.WINDOW_POPUP)
    self.dockAlignment     = None
    self.icons             = None
    

    ## Check layout setting and load right layout
    if self.pulseApplet.config.get("layout") == "vertical":
      self.pulseAppletStream = PulseAppletCompactStream
    elif self.pulseApplet.config.get("layout") == "horizontal":
      self.pulseAppletStream = PulseAppletHorizontalStream

    ## If "Use XML file" settings is On, get Icon path list
    if self.pulseApplet.config.get_bool("icon_use_xml") == True:    
      self.icons_xml = StreamIcons()
      self.icons_xml.loadStreamIcons(self.pulseApplet.config.get("distro"))
      self.icons = self.icons_xml.listAllStreamIcons()

    ## Create Dock and add it to window
    if self.pulseAppletStream.vertical:
      self.streams = gtk.HBox(False, 5)
    else:
      self.streams = gtk.VBox(False, 5)

    dockAlignment = gtk.Alignment(0.5, 0.5, 0, 0)
    dockAlignment.set_padding(2, 2, 2, 2)
    dockAlignment.add(self.streams)
    self.dockAlignment = dockAlignment

    dockFrame = gtk.Frame()
    dockFrame.set_shadow_type(gtk.SHADOW_OUT)
    dockFrame.add(dockAlignment)

    self.window.add(dockFrame)
    return

  ####
  ##
  ## Functions

  ## Clears dock when app is closed

  def clearStreamsBox(self):
    if self.streams != None:
      self.streams.foreach(self.streams.remove)
    return

  ###

  ## Adds No connections text to Dock if Applet doesn't have PA connection

  def addNoConnection(self, streamList):
    l = gtk.Label()
    l.set_markup("<span weight='bold'>" + _('No connection') + "</span>")
    streamList.add(l)

    return

  ###

  ##  Create Separator between Stream in Dock

  def addStreamSeparator(self, streamList, vertical_orientation):
    if vertical_orientation:
      streamSeparator = gtk.VSeparator()
    else:
      streamSeparator = gtk.HSeparator()

    streamList.add(streamSeparator)

    return

  ###

  ## Creates Stream and adds it to the dock

  def addStreamObject(self, streamList, pulseSink):
    icon = self.getIconPath(pulseSink.client.name)

    stream = self.pulseAppletStream(pulseSink,icon)

    stream.createStreamLayout(self.config.get_bool('show_vol_amount'))

    # This could be
    # stream = PulseAppletSimpleStream(pulseSink,
    #                                  self.stream_button_press_event,
    #                                  self.stream_value_change)
    
    ## Make mute button and volume bar connections 
    ## ( In vertical layout mute button is included to stream icon )
    stream.muteButton.connect("button-press-event",
                              self.stream_button_press_event,
                              pulseSink)

    stream.connect("button-press-event",
                   self.stream_button_press_event,
                   pulseSink)

    stream.volumeAdjust.connect("value-changed",
                                self.stream_value_change,
                                pulseSink)

    streamList.add(stream)

    return stream.get_size()

  ###

  ## Get Stream icon path
  def getIconPath(self,name):
    if self.icons != None and self.pulseAppletStream.vertical:
      import re
      for icon in self.icons:    
        if re.search(icon.name, name, re.I):
          return icon.path
    
    return ""


  ###
  #
  # Update dock content

  def update(self):
    height = 0
    width = 0
    sinks = []
    streamsAmount = 0

    streamList = None

    if self.pulseApplet.config.get("layout") == "vertical":
      self.pulseAppletStream = PulseAppletCompactStream
    elif self.pulseApplet.config.get("layout") == "horizontal":
      self.pulseAppletStream = PulseAppletHorizontalStream

    if self.pulseApplet.pulse.connected:
      sinks = self.pulseApplet.pulse.pulse_sink_input_list()

      if sinks == None:
        sinks = []

      clients = dict(map(lambda x: (x.index, x),
                     self.pulseApplet.pulse.pulse_client_list()))

      # Set client
      for sink in sinks:
        # Check is client existing
        if sink.client_id in clients.keys():
          sink.setClient(clients[sink.client_id])

    sinks.insert(0, self.pulseApplet.selected_sink)

    ## Streams amount, stream names, stream attributes needed
    streamsAmount = len(sinks)

    ## Update streamlist
    if not self.pulseApplet.pulse.connected:
      streamList = gtk.VBox(False, 5)
      self.addNoConnection(streamList)
      height = 20 # Height of NoConnection dock
      width = 200 # Width  of NoConnection dock
    else:
      if self.pulseAppletStream.vertical:
        streamList = gtk.HBox(False, 5)
        width += 1
      else:
        streamList = gtk.VBox(False, 5)
        height += 1

      streamNumber = 1

      for sink in sinks:
        #print sink
        (streamWidth, streamHeight) = self.addStreamObject(streamList, sink)

        #print (streamWidth, streamHeight)
        ## Create Horizontal bar in the end if stream isn't last one
        #if streamNumber < streamsAmount:
        if streamNumber == 1 and streamsAmount > 1:
          self.addStreamSeparator(streamList, self.pulseAppletStream.vertical)
          if self.pulseAppletStream.vertical:
            width += 6
          else:
            height += 6

        streamNumber = streamNumber + 1

        if self.pulseAppletStream.vertical:
          height = streamHeight
          width += streamWidth
        else: 
          height += streamHeight
          width = streamWidth

    # Remove all elements (one only)
    self.dockAlignment.remove(self.streams)

    self.streams = streamList
    self.dockAlignment.add(streamList)
    self.window.resize(width, height)
    #print "a", (width, height)

    ##Update position
    self.updatePosition()
    
    return

  ###
  
  ## Update dock position

  def updatePosition(self):
    self.window.set_position(gtk.WIN_POS_MOUSE)

    # Current position on screen
    (appletX, appletY) = self.pulseApplet.applet.window.get_origin()
    (appletWidth, appletHeight) = self.pulseApplet.applet.window.get_size()
    (dockWidth, dockHeight) = self.window.get_size()

    screenWidth = gtk.gdk.screen_width()
    screenHeight = gtk.gdk.screen_height()

    orient = self.pulseApplet.applet.get_orient()

    if orient == gnomeapplet.ORIENT_UP:
      # CENTER
      if (screenWidth - dockWidth) < appletX:
        # LEFT
        x = appletX - dockWidth + appletWidth
      else:
        # RIGHT
        x = appletX

      y = appletY - dockHeight

    elif orient == gnomeapplet.ORIENT_RIGHT:
      x = appletX + appletWidth
      # CENTER
      if (screenHeight - dockHeight) > appletY:
        # DOWN
        y = appletY
      else:
        # UP
        y = appletY + appletHeight - dockHeight


    elif orient == gnomeapplet.ORIENT_DOWN:
      # CENTER
      if (screenWidth - dockWidth) < appletX:
        # LEFT
        x = appletX - dockWidth + appletWidth
      else:
        # RIGHT
        x = appletX

      y = appletY + appletHeight

    else:
      x = appletX - dockWidth
      # CENTER
      if (screenHeight - dockHeight) > appletY:
        # DOWN
        y = appletY
      else:
        # UP
        y = appletY + appletHeight - dockHeight

    self.window.move(x,y)

    return

  ####
  #
  # Events

  ## Mouse button events

  def stream_button_press_event(self, widget, event, pulseSink):
    button_icon = gtk.Image()

    if isinstance(widget, PulseAppletStream):
      if event.button != 2:
        return True

      button = widget.muteButton
    else:
      button = widget
    
    #print type(pulseSink)

    ## widget.muted tells streams state 
    if pulseSink.mute == 0:
      pulseSink.muteStream(self.pulseApplet.pulse)
    else:
      pulseSink.unmuteStream(self.pulseApplet.pulse)
    icon = self.getIconPath(pulseSink.client.name)

    button.set_image(self.pulseAppletStream.getIcon(pulseSink.client.name,
                                                    pulseSink.mute, icon))

    if isinstance(pulseSink, PulseSinkInfo):
      # Update icon and icon's tooltip
      self.pulseApplet.updateIcon()

    return True

  #####
  ##
  ## Functions

  ## Change Stream audio volume

  def stream_value_change(self, widget, pulseSink):
    #print "stream_value_change: callback"

    pulseSink.setVolume(self.pulseApplet.pulse,
                        PulseVolume(int(widget.get_value())))

    # FIXME pulseSink.setVolume should identify is given param int or
    # PulseVolume
    #pulseSink.setVolume(self.pulseApplet.pulse, int(widget.get_value()))


    if isinstance(pulseSink, PulseSinkInfo):
      # Update icon and icon's tooltip
      self.pulseApplet.updateIcon()

    return

################################################################################
#
# PulseAppletStream

class PulseAppletStream(gtk.EventBox):

  def __init__(self, pulseSink,iconPath):
    gtk.EventBox.__init__(self)

    # Variables
    self.pulseSink    = pulseSink
    self.muteButton   = None
    self.volumeAdjust = None
    self.iconPath     = iconPath

  ###
  
  vertical = None

  #####
  ##
  ## Functions

  def get_size(self):
    return (-1, -1)

  ###

  # NOTE: Class-method, static-method etc.
  def getIcon(sinkName, isMuted):
    icon = gtk.Image()

    if isMuted:
      icon.set_from_icon_name('audio-volume-muted', gtk.ICON_SIZE_BUTTON)
    else:
      icon.set_from_icon_name('audio-volume-medium', gtk.ICON_SIZE_BUTTON)

    return icon

  getIcon = staticmethod(getIcon)

  ######
  ##
  ## Events

  ## Scrollwheel event ( When scrollwheel is used above this stream )

  def stream_scroll_event(self, widget, event):   
    #print "stream_scroll_event: Called"

    volume = self.volumeAdjust.get_value()

    if event.direction == gtk.gdk.SCROLL_UP or \
       event.direction == gtk.gdk.SCROLL_RIGHT:
      volume += 10
    elif event.direction == gtk.gdk.SCROLL_DOWN or \
         event.direction == gtk.gdk.SCROLL_LEFT:
      volume -= 10

    if volume > 100:
      volume = 100
    elif volume < 0:
      volume = 0

    self.updateAppTooltip(self.pulseSink.client.name, volume) 
    self.volumeAdjust.set_value(volume)

    return True
    
  ###
  
  ## Increase Volume (+) Button event

  def stream_increase_volume_event(self, widget, event):
    #print "stream_scroll_event: Called"

    volume = self.volumeAdjust.get_value() + 10

    if volume > 100:
      volume = 100

    self.updateAppTooltip(self.pulseSink.client.name, volume)
    self.volumeAdjust.set_value(volume)

    return True
    
    
  ###

  ## Decrease Volume (-) Button event
  
  def stream_decrease_volume_event(self, widget, event):
    #print "stream_scroll_event: Called"

    volume = self.volumeAdjust.get_value() - 10

    if volume < 0:
      volume = 0

    self.updateAppTooltip(self.pulseSink.client.name, volume)
    self.volumeAdjust.set_value(volume)

    return True

  ###

  ## Volume Bar change event
  
  def stream_slider_changed(self,range,widget,new_value):
    self.updateAppTooltip(self.pulseSink.client.name, new_value)
    self.volumeAdjust.set_value(int(new_value)) 

    return True



################################################################################
#
# PulseAppletHorizontalStream ( Uses PulseAppletStream as base )

class PulseAppletHorizontalStream(PulseAppletStream):
  def __init__(self, pulseSink,iconPath):
    PulseAppletStream.__init__(self, pulseSink,iconPath)
    return

  ###

  vertical = False

  #####
  ##
  ## Functions

  ## Streams size

  def get_size(self):
    return (202, 66)

  ###

  ## Create Stream layout
  #  Horizontal stream layout consist of Stream name and client name, Mute button 
  #  and volume bar.

  def createStreamLayout(self,showVolumeAmount):
    vbox = gtk.VBox()
    
    ## Create Client name if exists ( This can be for example Bashee )
    if self.pulseSink.client != None:
      streamClientLabel = gtk.Label()
      streamClientLabel.set_markup("<span size='small' weight='bold'>" +
                                   _(self.pulseSink.client.name) + ":</span>")
      streamClientLabel.set_alignment(0,0.5)
      vbox.add(streamClientLabel)
    
    ## Create Stream name 
    streamName = self.pulseSink.name    
    if len(streamName) >= 30:
      streamName = streamName[0:27] + "..."
    
    streamLabel = gtk.Label()
    streamLabel.set_markup("<span size='x-small'>" + streamName + "</span>")
    streamLabel.set_alignment(0,0.5)
  
    vbox.add(streamLabel)
    
    ## Create Controls
    controls = gtk.HBox()

    ## Create Mute/UnMute button
    muteButton = gtk.Button()
    muteButtonIcon = gtk.Image()
    
    muteButton.set_image(PulseAppletHorizontalStream.getIcon(
                                self.pulseSink.client.name,
                                self.pulseSink.mute)
                         )
    
    controls.add(muteButton)
    
    
    ## Create Volumebar
    volumeLevel = sum(self.pulseSink.volume.values) / \
                  len(self.pulseSink.volume.values)
    volumeAdjust = gtk.Adjustment(volumeLevel, 0, 100, -5)

    streamVolume = gtk.HScale(volumeAdjust)
    streamVolume.set_digits(0)
    streamVolume.set_property("width-request", 160)  
    streamVolume.set_value_pos(gtk.POS_RIGHT)
    if showVolumeAmount == False:
      streamVolume.set_draw_value(False)
    controls.add(streamVolume)

    self.muteButton = muteButton
    self.volumeAdjust = volumeAdjust
    
    vbox.add(controls)

    ## Add whole thing to stream layout

    self.add(vbox)

    ## Make Connections

    self.connect("scroll-event",
                 self.stream_scroll_event)

    return

  ###

################################################################################
#
# PulseAppletCompactStream ( Uses PulseAppletStream as base )

class PulseAppletCompactStream(PulseAppletStream):
  def __init__(self, pulseSink, iconPath):
    PulseAppletStream.__init__(self, pulseSink, iconPath)
    return

  ###
  vertical = True

  #####
  ##
  ## Functions

  ## Stream size
  def get_size(self):
    return (34, 190)

  ###

  ## Get Icon ( this makes get icon as )
  # NOTE: Class-method, static-method etc.
  def getIcon(sinkName, isMuted, iconPath):
    icon = gtk.Image()

    import re
    ## FIXME: Make better if-else structure!
    if iconPath != "":
        icon = PulseAppletCompactStream.createIcon(iconPath, isMuted)
    elif re.search("selected sink", sinkName, re.I):
      if isMuted:
        icon.set_from_icon_name('audio-volume-muted', gtk.ICON_SIZE_MENU)
      else:
        icon.set_from_icon_name('audio-volume-high', gtk.ICON_SIZE_MENU) 
    else:
      if isMuted:
        icon.set_from_icon_name('audio-volume-muted', gtk.ICON_SIZE_MENU)
      else:
        icon.set_from_icon_name('audio-volume-low', gtk.ICON_SIZE_MENU)
    
    return icon
  
  getIcon = staticmethod(getIcon)

  ###
  
  ## Create icon pixmap from icon filename (sinkFileName variable)
  # NOTE: Class-method, static-method etc.
  def createIcon(sinkFileName, isMuted):
    icon = gtk.Image()
    if(os.path.isfile(sinkFileName)):
    
      iconbuf = gtk.gdk.pixbuf_new_from_file_at_size(sinkFileName,16,16)
      if isMuted == True:
        mutedbuf = gtk.gdk.pixbuf_new_from_file_at_size(MUTED_ICON, 8, 8)
        mutedbuf.composite(iconbuf, 8, 8, 8, 8, 8, 8, 1, 1,
                         gtk.gdk.INTERP_NEAREST, 255)
        icon.set_from_pixbuf(iconbuf)
      else:
        icon.set_from_pixbuf(iconbuf)
    
    else:
      if isMuted:
        icon.set_from_icon_name('audio-volume-muted', gtk.ICON_SIZE_MENU)
      else:
        icon.set_from_icon_name('audio-volume-low', gtk.ICON_SIZE_MENU)
    
    return icon
  
  createIcon = staticmethod(createIcon)

  ###

  ## Create mutedIcon from

  ## Update Stream icons/Mute buttons Tooltip to stream name
  
  def updateAppTooltip(self, sinkName, volume):
    self.icon_tooltip.set_tip(self.muteButton, sinkName)
    return
 
  ###

  ## Create layout
  #  Vertical layout consist of Stream Name/Mute Button which has Stream name
  #  as tooltip and volume slider    
  def createStreamLayout(self,showVolumeAmount):

    # Layout here
    vbox = gtk.VBox()
    
    
    ## Sink icon with mute volume button and stream name tooltip
    muteButton = gtk.Button()
    self.icon_tooltip = gtk.Tooltips()

    muteButtonIcon = PulseAppletCompactStream.getIcon(
                                self.pulseSink.client.name,
                                self.pulseSink.mute,self.iconPath)

    muteButton.set_property("can-focus", False)
    muteButton.set_image(muteButtonIcon)
    muteButton.set_relief(gtk.RELIEF_NONE)

    vbox.pack_start(muteButton, False, False, 2)
    self.muteButton = muteButton
    self.updateAppTooltip(self.pulseSink.client.name,
                          self.pulseSink.volume.getVolume())  
    
    
    ## Volume slider
    volumeLevel = sum(self.pulseSink.volume.values) / \
                  len(self.pulseSink.volume.values)
    volumeAdjust = gtk.Adjustment(volumeLevel, 0, 100, 5)

    streamVolume = gtk.VScale(volumeAdjust)
    streamVolume.set_property("height-request", 150)
    streamVolume.set_draw_value(True)
    streamVolume.set_digits(0)   
    streamVolume.set_value_pos(gtk.POS_BOTTOM)
    if showVolumeAmount == False:
      streamVolume.set_draw_value(False)
    streamVolume.set_inverted(True)
    vbox.pack_start(streamVolume, True, False, 0)

    ## Add these to stream layout
    self.volumeAdjust = volumeAdjust
    self.add(vbox)
 
    ## Make connections 
    streamVolume.connect("change-value",self.stream_slider_changed)

    self.connect("scroll-event",
                 self.stream_scroll_event)

    return

  ###

################################################################################
#
# MAIN

def pulse_applet_factory(applet, iid):
  applet.version = APPLET_VERSION
  PulseApplet(applet, iid)
  return True

# This is needed,
# because gnome.applet.Applet.get_orient() doens't work properly 
# This creates new_get_orient which is fixed get_orient
def fixed_orient(pulseApplet):
  def new_get_orient():
    #width  = gtk.gdk.screen_width()
    height = gtk.gdk.screen_height()
        
    (appletX, appletY) = pulseApplet.applet.window.get_origin()

    #print (appletX, appletY)

    if appletY > height / 2:
      return gnomeapplet.ORIENT_UP
    #else:

    return gnomeapplet.ORIENT_DOWN

  return new_get_orient

###
#
#  Window mode
#
if "run-in-window" in sys.argv or "window" in sys.argv:
  main_window = gtk.Window(gtk.WINDOW_TOPLEVEL)
  main_window.set_title("Pulse Applet")
  main_window.connect("destroy", gtk.main_quit)

  app = gnomeapplet.Applet()
  app.version = APPLET_VERSION

  pulseApplet = PulseApplet(app, None)
  pulseApplet.allow_quit = True
  #
  app.reparent(main_window)
  main_window.show()

  app.get_orient = fixed_orient(pulseApplet)

  gtk.main()

###
#
# Tray mode
#
elif "tray" in sys.argv:
  import egg.trayicon
  trayIcon = egg.trayicon.TrayIcon(_("Pulse Applet"))
  app = gnomeapplet.Applet()
  app.version = APPLET_VERSION

  pulseApplet = PulseApplet(app, None)
  pulseApplet.allow_quit = True
  #
  app.reparent(trayIcon)
  trayIcon.show()

  app.get_orient = fixed_orient(pulseApplet)

  gtk.main()

###
#
# Applet / normal mode
#
else:
  #import sys
  #sys.stdout = open("/tmp/pulse-applet.log", "w")
  #sys.stderr = open("/tmp/pulse-applet.log", "w+")

  gnomeapplet.bonobo_factory("OAFIID:GNOME_PulseApplet", 
                             gnomeapplet.Applet.__gtype__, 
                             "pulse-applet", "0", pulse_applet_factory)
